﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using System.Text;
using System.IO;
using System.Configuration;

namespace Compilador.Lexico
{
    class CharBuffer
    {
        #region Properties
        private int field;
        /// <summary>
        /// Getter Fila
        /// </summary>
        public int Field
        {
            get { return field; }
        }
        private int column;
        /// <summary>
        /// Getter Columna
        /// </summary>
        public int Column
        {
            get { return column; }
        }

        private bool finArchivo = false;
        /// <summary>
        /// Getter Fin de Archivo
        /// </summary>
        public bool FinArchivo
        {
            get { return finArchivo; }
        }

        private bool habiaEspacio;
        /// <summary>
        /// Getter Habia Espacio
        /// </summary>
        public bool HabiaEspacio
        {
            get { return habiaEspacio; }
        }

        private int offsetArchivo;
        private char[] charBuffer;
        private int charBufferMaxSize;
        private int charBufferTope;
        private bool ultimoBuffer = false;
        private int indiceBuffer;
        private String path;
        private int extraEspaciosEnBuffer;
        #endregion Properties
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="path"></param>
        public CharBuffer(String path)
        {
            this.path = path;
            this.field = 1;
            this.column = 0;
            this.offsetArchivo = 0;
            this.charBufferMaxSize = 100;
            ultimoBuffer = false;
            finArchivo = false;
            //es para que el look ahead en la ultima posicion del buffer no se pierda al cambiar el buffer
            this.extraEspaciosEnBuffer = 1;
            this.indiceBuffer = 0;
            this.LlenarBuffer();
        }
        /// <summary>
        /// Llenar Buffer
        /// </summary>
        private void LlenarBuffer()
        {
            try
            {
                if (!ultimoBuffer)
                {
                    this.indiceBuffer = 0;
                    StreamReader strReader = new StreamReader(path);
                    //Creo el buffer con la capacidad elegida y unos caracteres mas.
                    charBuffer = new char[this.charBufferMaxSize + this.extraEspaciosEnBuffer];
                    strReader.BaseStream.Seek(offsetArchivo, SeekOrigin.Begin);
                    //Le resto la cantidad de espacios extras al tope, asi el offset del archivo 
                    // queda esa cantidad de caracteres retrasados, para volverlos a leer cuando haga el switch
                    // del buffer.
                    this.charBufferTope = strReader.Read(charBuffer, 0, charBuffer.Length) - this.extraEspaciosEnBuffer;
                    offsetArchivo += this.charBufferTope;
                    if (this.charBufferTope < (charBuffer.Length - this.extraEspaciosEnBuffer))
                    {
                        this.ultimoBuffer = true;
                    }
                    //String aux = strReader.ReadToEnd();
                    strReader.Close();
                }
                else
                {
                    this.finArchivo = true;
                }
            }
            catch (Exception ex)
            {
                Utils.Log.AddError(ex.Message);
                throw new Exception("Error al rellenar el buffer");
            }
            //return aux;
        }
        /// <summary>
        /// Obtener Proximo Caracter Valido
        /// </summary>
        /// <returns></returns>
        public char ObtenerProximoCaracterValido()
        {
            char x;
            this.habiaEspacio = false;
            //Los caracteres salteables no tienen importancia
            x = this.ObtenerProximoChar();
            while (CharHelper.esCaracterSalteable(x))
            {
                this.habiaEspacio = true;
                if (CharHelper.esRetornoDeCarro(x))
                {
                    x = this.ObtenerProximoChar();
                    if (CharHelper.esNuevaLinea(x))
                    {
                        field++;
                        column = 0;
                    }
                }
                x = this.ObtenerProximoChar();
            }
            return x;
        }
        /// <summary>
        /// Obtener Proximo Char
        /// </summary>
        /// <returns></returns>
        public char ObtenerProximoChar()
        {
            char x = this.PeekProximoChar();
            indiceBuffer++;
            column++;
            return x;
        }
        /// <summary>
        /// Pispear Proximo Char
        /// </summary>
        /// <returns></returns>
        public char PeekProximoChar()
        {
            //Si quiere pispear y el buffer se acabo
            if (indiceBuffer >= this.charBufferTope)
            {
                this.LlenarBuffer();
            }
            char x = this.charBuffer[indiceBuffer];
            return x;
        }
    }
}
